package environment;

import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.junit.*;

import environment.borders.*;
import environment.borders.Void;
import temperature.Temperature;
import temperature.TemperatureUnit;

/**
 * Een testklasse voor de klasse Square.
 * Deze bevat alle testmethoden om setters en speciale mutators
 * te controleren van de klasse 'vakje'.
 * 
 * @author 	Nathan Bekaert & Philippe de Potter de ten Broeck
 * @version 2.0
 */
public class SquareTest {
	
	Square square1;
	Square square2;
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}
	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}
	@Before
	public void setUp() throws Exception {
		square1 = new Square();
		square2 = new Square(Temperature.CELSIUS_0,new BigDecimal(50.00));
	}
	@After
	public void tearDown() throws Exception {
	}
	
	
	//TEMPERATUUR (Defensief)
	@Test
	public void testTemperatureLimits(){
		assertEquals(square1.getLowerLimitTemperature().getTemperatureValueIn(TemperatureUnit.CELSIUS).compareTo(new BigDecimal(-200)),0);
		assertEquals(square1.getUpperLimitTemperature().getTemperatureValueIn(TemperatureUnit.CELSIUS).compareTo(new BigDecimal(5000)),0);
	}
	@Test
	public void testChangedTemperatureLimits() throws Exception{
		square1.setLimitsTemperature(new Temperature(BigDecimal.ZERO,TemperatureUnit.CELSIUS), 
				new Temperature(new BigDecimal(200),TemperatureUnit.CELSIUS));
		assertEquals(square1.getLowerLimitTemperature().getTemperatureValueIn(TemperatureUnit.CELSIUS).compareTo(new BigDecimal(0)),0);
		assertEquals(square1.getUpperLimitTemperature().getTemperatureValueIn(TemperatureUnit.CELSIUS).compareTo(new BigDecimal(200)),0);
	}
	@Test(expected=IllegalArgumentException.class)
	public void testChangedTemperatureLimitsNotMatching() throws Exception{
		square1.setLimitsTemperature(new Temperature(new BigDecimal(500),TemperatureUnit.CELSIUS), 
				new Temperature(new BigDecimal(100),TemperatureUnit.CELSIUS));
	}
	@Test(expected=IllegalArgumentException.class)
	public void testChangedTemperatureLimitsNullUnderLimit() throws Exception{
		square1.setLimitsTemperature(null, 
				new Temperature(new BigDecimal(100),TemperatureUnit.CELSIUS));
	}
	@Test(expected=IllegalArgumentException.class)
	public void testChangedTemperatureLimitsNullUpperLimit() throws Exception{
		square1.setLimitsTemperature(new Temperature(new BigDecimal(500),TemperatureUnit.CELSIUS)
				,null);
	}
	@Test
	public void testTemperature(){
		assertEquals(square1.getTemperature().getTemperatureValueIn(TemperatureUnit.CELSIUS).compareTo(BigDecimal.ZERO),0);
	}
	@Test 
	public void testChangedTemperatureConvertToLimit(){
		square1.alterTemperature(new Temperature(new BigDecimal(5273), TemperatureUnit.KELVIN));
		assertEquals(square1.getTemperature().getTemperatureValueIn(TemperatureUnit.KELVIN).compareTo(new BigDecimal(5273)),0);
	}
	@Test
	public void testChangedTemperature(){
		square1.alterTemperature(new Temperature(BigDecimal.ONE, TemperatureUnit.FAHRENHEIT));
		assertEquals(square1.getTemperature().getTemperatureValueIn(TemperatureUnit.FAHRENHEIT).compareTo(BigDecimal.ONE),0);
	}
	@Test(expected=IllegalArgumentException.class)
	public void testChangedTemperatureTooLow(){
		square1.alterTemperature(new Temperature(BigDecimal.ONE, TemperatureUnit.KELVIN));
	}
	@Test(expected=IllegalArgumentException.class)
	public void testChangedTemperatureTooHigh(){
		square1.alterTemperature(new Temperature(new BigDecimal(9400), TemperatureUnit.FAHRENHEIT));
	}
	@Test(expected=IllegalArgumentException.class)
	public void testChangedTemperatureFalseConversion(){
		square1.alterTemperature(new Temperature(new BigDecimal(30), TemperatureUnit.KELVIN));
	}
	
	//SCHADE door temp (defensief)
	
	@Test
	public void testColdDamage()throws Exception{
		square1.alterTemperature(new Temperature(new BigDecimal(20), TemperatureUnit.CELSIUS));
		assertEquals(square1.coldDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(-5), TemperatureUnit.CELSIUS));
		assertEquals(square1.coldDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(-14), TemperatureUnit.CELSIUS));
		assertEquals(square1.coldDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(-15), TemperatureUnit.CELSIUS));
		assertEquals(square1.coldDamage(), 1);
		square1.alterTemperature(new Temperature(new BigDecimal(-16), TemperatureUnit.CELSIUS));
		assertEquals(square1.coldDamage(), 1);
		square1.alterTemperature(new Temperature(new BigDecimal(-91), TemperatureUnit.CELSIUS));
		assertEquals(square1.coldDamage(), 8);
	}
	@Test
	public void testHeatDamage()throws Exception{
		square1.alterTemperature(new Temperature(new BigDecimal(20), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(35), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(49), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(50), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 1);
		square1.alterTemperature(new Temperature(new BigDecimal(51), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 1);
		square1.alterTemperature(new Temperature(new BigDecimal(118), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 5);
	}
	@Test (expected=ArithmeticException.class)
	public void testHeatDamageOutOfUpperBound() throws Exception{
		square1.setLimitsTemperature(square1.getLowerLimitTemperature(),
				new Temperature(new BigDecimal(Long.MAX_VALUE), TemperatureUnit.CELSIUS));
		square1.alterTemperature(square1.getUpperLimitTemperature());
		square1.heatDamage();
	}
	@Test
	public void testHeatDamageChanged()throws Exception{
		square1.setHeatDamageSlope(new BigDecimal(20));
		square1.setHeatDamageBorder(new Temperature(new BigDecimal(50), TemperatureUnit.CELSIUS));
		square1.alterTemperature(new Temperature(new BigDecimal(30), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(50), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(69), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 0);
		square1.alterTemperature(new Temperature(new BigDecimal(70), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 1);
		square1.alterTemperature(new Temperature(new BigDecimal(71), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 1);
		square1.alterTemperature(new Temperature(new BigDecimal(181), TemperatureUnit.CELSIUS));
		assertEquals(square1.heatDamage(), 6);
	}
	
	
	//Vochtigheid en roest(nominaal)
	
	@Test 
	public void testHumidity(){
		assertTrue(square1.getHumidity().compareTo(new BigDecimal(BigInteger.valueOf(1000),2))==0);
	}
	@Test 
	public void testHumidityChanged(){
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(8000),2));
		assertTrue(square1.getHumidity().compareTo(new BigDecimal(BigInteger.valueOf(8000),2))==0);
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(10000),2));
		assertTrue(square1.getHumidity().compareTo(new BigDecimal(BigInteger.valueOf(1000),1))==0);
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(0000),2));
		assertTrue(square1.getHumidity().compareTo(new BigDecimal(BigInteger.valueOf(000),2))==0);
	}
	@Test 
	public void testHumidityPres(){
		assertFalse(Square.isValidHumidity(new BigDecimal(BigInteger.valueOf(-5000),2)));
		assertFalse(Square.isValidHumidity(new BigDecimal(BigInteger.valueOf(20000),2)));
	}
	@Test
	public void testRustDamage(){
		assertEquals(square1.rustDamage(),0);
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(10000),2));
		assertEquals(square1.rustDamage(),10);
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(3700),2));
		assertEquals(square1.rustDamage(),1);
	}

	
	//Gladheid (defensief):

	@Test 
	public void testSlippery(){
		assertFalse(square1.isSlippery());
		assertFalse(square1.isIceSlippery());
		assertFalse(square1.isWaterSlippery());
		assertFalse(square1.getSlipperyMaterial());
	}
	@Test 
	public void testSlipperyWater(){
		square1.alterTemperature(Temperature.CELSIUS_1);
		assertFalse(square1.isSlippery());
		assertFalse(square1.isIceSlippery());
		assertFalse(square1.isWaterSlippery());
		assertFalse(square1.getSlipperyMaterial());
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(9900),2));
		assertFalse(square1.isSlippery());
		assertFalse(square1.isIceSlippery());
		assertFalse(square1.isWaterSlippery());
		assertFalse(square1.getSlipperyMaterial());
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(10000),2));
		assertTrue(square1.isSlippery());
		assertFalse(square1.isIceSlippery());
		assertTrue(square1.isWaterSlippery());
		assertFalse(square1.getSlipperyMaterial());
	}
	@Test 
	public void testSlipperyIce(){
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(2000),2));
		assertTrue(square1.isSlippery());
		assertTrue(square1.isIceSlippery());
		assertFalse(square1.isWaterSlippery());
		assertFalse(square1.getSlipperyMaterial());
		square1.alterTemperature(Temperature.CELSIUS_1);
		assertFalse(square1.isSlippery());
		assertFalse(square1.isIceSlippery());
		assertFalse(square1.isWaterSlippery());
		assertFalse(square1.getSlipperyMaterial());
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(5000),2));
		assertFalse(square1.isSlippery());
		assertFalse(square1.isIceSlippery());
		assertFalse(square1.isWaterSlippery());
		assertFalse(square1.getSlipperyMaterial());
		square1.alterTemperature(Temperature.CELSIUS_0);
		assertTrue(square1.isSlippery());
		assertTrue(square1.isIceSlippery());
		assertFalse(square1.isWaterSlippery());
		assertFalse(square1.getSlipperyMaterial());
	}
	@Test 
	public void testSlipperyMaterial(){
		square1.setSlipperyMaterialSlippery(true);
		assertTrue(square1.isSlippery());
		assertFalse(square1.isIceSlippery());
		assertFalse(square1.isWaterSlippery());
		assertTrue(square1.getSlipperyMaterial());
	}
	
	
	//Bewoonbaarheid (defensief):
	
	@Test 
	public void testInhabitability(){
		assertTrue(square1.getInhabitability().compareTo(new BigDecimal(BigInteger.valueOf(000),2)) == 0);
	}
	@Test 
	public void testInhabitabilityChanged(){
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(7600),2));
		square1.alterTemperature(new Temperature(new BigDecimal(BigInteger.valueOf(500),1), TemperatureUnit.CELSIUS));
		assertTrue(square1.getInhabitability().compareTo(new BigDecimal(BigInteger.valueOf(-2),1)) == 0);
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(2000),2));
		square1.alterTemperature(new Temperature(new BigDecimal(BigInteger.valueOf(-150),1), TemperatureUnit.CELSIUS));
		assertTrue(square1.getInhabitability().compareTo(new BigDecimal(BigInteger.valueOf(-10),1)) == 0);
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(10000),2));
		square1.setLimitsTemperature(square1.getLowerLimitTemperature(), new Temperature(new BigDecimal(Long.MAX_VALUE), TemperatureUnit.CELSIUS));
		square1.alterTemperature(new Temperature(new BigDecimal(Integer.MAX_VALUE), TemperatureUnit.CELSIUS));
		assertTrue(square1.getInhabitability().compareTo(new BigDecimal(-1713002111395.282226562500)) == 0);
	}
	@Test (expected=ArithmeticException.class)
	public void testInhabitabilityOutOfUpperBound() throws Exception{
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(1000),2));
		square1.setLimitsTemperature(square1.getLowerLimitTemperature(), 
				new Temperature(new BigDecimal(Long.MAX_VALUE), TemperatureUnit.CELSIUS));
		square1.alterTemperature(new Temperature(new BigDecimal(Long.MAX_VALUE), TemperatureUnit.CELSIUS));
		square1.getInhabitability();
	}
	@Test (expected=IllegalArgumentException.class)
	public void testInhabitabilityOutOfLowerBound() throws Exception{
		square1.alterHumidity(new BigDecimal(BigInteger.valueOf(1000),2));
		square1.setLimitsTemperature(new Temperature(new BigDecimal(Long.MIN_VALUE), TemperatureUnit.CELSIUS),
				square1.getUpperLimitTemperature());
		square1.alterTemperature(new Temperature(new BigDecimal(Long.MIN_VALUE), TemperatureUnit.CELSIUS));
		square1.getInhabitability();
	}
	@Test
	public void testInhabitabilityHumidityOutOfBound() throws Exception{
		BigDecimal newHumidity = new BigDecimal(BigInteger.valueOf(100000),2);
		assertFalse(Square.isValidHumidity(newHumidity));
	}
	
	/**
	 * Test de associatie tussen Square en Border:
	 *	Bekeken vanuit een enkel vakje; geen interconnectie tussen vakjes.
	 * 	- Creatie
	 * 	- BuildNewBorder
	 * 	- BreakDownBorder
	 * 	- Square.Terminate
	 * 	- Border.Terminate
	 * 	- 
	 */
	@Test
	public void testAssociationSquareBorders() {
		Square square1 = new Square();
		Square square2 = new Square(Temperature.CELSIUS_1, BigDecimal.ONE, Direction.NORTH, Direction.WEST);
		assertTrue(square1.getTemperature().compareTo(Temperature.CELSIUS_0)==0);
		assertTrue(square1.getHumidity().compareTo(BigDecimal.TEN)==0);
		assertTrue(square2.getTemperature().compareTo(Temperature.CELSIUS_1)==0);
		assertTrue(square2.getHumidity().compareTo(BigDecimal.ONE)==0);
		assertTrue(square1.getBorderAt(Direction.FLOOR) instanceof Wall);
		assertTrue(square1.getBorderAt(Direction.CEILING) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.NORTH) instanceof Wall);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Wall);
		assertTrue(square2.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.SOUTH) instanceof Void);
		
		Border door1 = new Door(false);
		Border door2 = new Door(true);
		square1.buildNewBorderIn(door1, Direction.EAST);
		square2.buildNewBorderIn(door2, Direction.EAST);
		assertTrue(square1.getBorderAt(Direction.EAST) instanceof Door);
		assertTrue(square2.getBorderAt(Direction.EAST) instanceof Door);
		
		square2.breakBorderDownIn(Direction.EAST);
		square2.breakBorderDownIn(Direction.WEST);
		assertTrue(square2.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Void);
		
		square1.terminate();
		assertTrue(square1.isTerminated());
		for(Direction direction: Direction.values())
			assertTrue(square1.getBorderAt(direction) == null);
		assertTrue(door1.isTerminated());
		assertTrue(door1.getFirstSquare() == null);
		assertTrue(door1.getSecondSquare( )== null);
		
		Border wall1 = new Wall(true);
		square2.buildNewBorderIn(wall1, Direction.EAST);
		Border wall2 = square2.getBorderAt(Direction.NORTH);
		assertTrue(wall2.hasSquare());
		assertTrue((wall2.getFirstSquare()==square2) || (wall2.getSecondSquare()==square2));
		wall2.terminate();
		assertTrue(wall2.isTerminated());
		assertTrue(wall2.getFirstSquare() == null);
		assertTrue(wall2.getSecondSquare( )== null);
		assertTrue(square2.getBorderAt(Direction.NORTH) instanceof Void);
		wall1.terminate();
		assertTrue(wall1.isTerminated());
		assertTrue(wall1.getFirstSquare() == null);
		assertTrue(wall1.getSecondSquare( )== null);
		assertFalse(square2.isTerminated());
		Border newWall = square2.getBorderAt(Direction.EAST);
		assertTrue(newWall instanceof Wall);
		assertFalse(wall1.hasSquare());
		assertFalse(newWall.isTerminated());
		assertTrue(newWall.hasAsSquare(square2));
		assertTrue(((Wall) newWall).isSlippery());
		assertFalse(wall1 == newWall);
	}
	
	@Test
	public void testBreakBorderDownNormalCase() {
		Wall wall = new Wall(false);
		square1.setStateUsed();
		square2.setStateUsed();
		square1.buildNewBorderIn(wall, Direction.EAST);
		square1.setNeighbourInDirection(square2, Direction.EAST);
		assertSame(square2, square1.getNeighbourIn(Direction.EAST));
		assertSame(square1, square2.getNeighbourIn(Direction.WEST));
		square1.breakBorderDownIn(Direction.EAST);
		assertSame(square2, square1.getNeighbourIn(Direction.EAST));
		assertTrue(square1.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Void);
		assertSame(square1.getBorderAt(Direction.EAST), square2.getBorderAt(Direction.WEST));
	}
	
	@Test
	public void testBreakBorderDownInNormalCase() {
		Wall wall = new Wall(false);
		square1.setStateUsed();
		square2.setStateUsed();
		square1.buildNewBorderIn(wall, Direction.EAST);
		square1.setNeighbourInDirection(square2, Direction.EAST);
		assertSame(square2, square1.getNeighbourIn(Direction.EAST));
		square1.breakBorderDownIn(Direction.EAST);
		assertSame(square2, square1.getNeighbourIn(Direction.EAST));
		assertTrue(square1.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Void);
		assertSame(square1.getBorderAt(Direction.EAST), square2.getBorderAt(Direction.WEST));
	}
	
	/**
	 * Test de associatie tussen Square's en Border's:
	 *	De nadruk ligt hierbij tussen meerdere squares.
	 * 	- setSquareInDirection
	 */
	/**@Test
	public void testComplicatedAssociationSquareBorders(){
		Square square1 = new Square(Temperature.CELSIUS_1, BigDecimal.TEN, Direction.NORTH, Direction.SOUTH, Direction.WEST);
		Square square2 = new Square();
		Square square3 = new Square(Temperature.CELSIUS_1, BigDecimal.TEN, Direction.NORTH, Direction.EAST, Direction.SOUTH);		
		Border door = new Door(false);
		square2.buildNewBorderIn(door, Direction.EAST);
		square1.setNeighbourInDirection(square2, Direction.EAST);
		square2.setNeighbourInDirection(square3, Direction.EAST);
		assertEquals(square1.getNeighbourIn(Direction.EAST),square2);
		assertTrue(square2.getNeighbourIn(Direction.EAST) == square3);
		assertTrue(square1.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.EAST) instanceof Door);
		assertTrue(square3.getBorderAt(Direction.WEST) instanceof Door);
		assertTrue(square1.getTemperature().compareTo(square2.getTemperature())==0);
		assertTrue(square1.getHumidity().compareTo(square2.getHumidity())==0);
		assertTrue(square2.getTemperature().compareTo(square3.getTemperature())!=0);
		assertTrue(square2.getHumidity().compareTo(square3.getHumidity())!=0);
		
		door.interact();
		System.out.println("Open door: ");
		System.out.println(square1);
		System.out.println(square2);
		System.out.println(square2.getBorders());
		System.out.println(square3);
		System.out.println();
		
		square2.removeNeighbourFrom(Direction.EAST);
		System.out.println("Remove square 3: ");
		System.out.println(square1);
		System.out.println(square2);
		System.out.println(square2.getBorders());
		System.out.println(square3);
		System.out.println();
	}*/
	@Test
	public void testTerminatedBorder() {
		Dungeon dungeon = new Dungeon();
		Square square1 = new Square();
		Square square2 = new Square();
		Border door = new Door(false);
		square1.buildNewBorderIn(door, Direction.EAST);
		dungeon.setSquareOnPosition(square1, new Position(0, 1, 2));
		dungeon.setSquareOnPosition(square2, new Position(1, 1, 2));
		assertTrue(door.hasAsSquare(square1));
		assertTrue(door.hasAsSquare(square2));
		door.terminate();
		assertTrue(door.isTerminated());
		assertFalse(door.hasSquare());
	}
	@Test
	public void testTerminatedBorderTerminate() {
		Dungeon dungeon = new Dungeon();
		Square square1 = new Square();
		Square square2 = new Square(Temperature.CELSIUS_1, BigDecimal.TEN, Direction.CEILING);
		dungeon.setSquareOnPosition(square1, new Position(0, 1, 2));
		dungeon.setSquareOnPosition(square2, new Position(0, 1, 1));
		Border wall1 = square1.getBorderAt(Direction.FLOOR);
		assertTrue(square2.getBorderAt(Direction.CEILING) == wall1);
		assertTrue(wall1.hasAsSquare(square1));
		assertTrue(wall1.hasAsSquare(square2));
		wall1.terminate();
		assertTrue(wall1.isTerminated());
		assertFalse(wall1.hasSquare());
		assertFalse(square1.isTerminated());
		assertFalse(square2.isTerminated());
		Border wall2 = square1.getBorderAt(Direction.FLOOR);
		assertFalse(wall2.isTerminated());
		assertTrue(square2.getBorderAt(Direction.CEILING) == wall2);
		assertTrue(wall2.hasAsSquare(square1));
		assertTrue(wall2.hasAsSquare(square2));
		assertTrue(wall2 instanceof Wall);
		assertFalse(((Wall) wall2).isSlippery());
		assertFalse(wall1 == wall2);
	}
}
